<?php

/**
 * 数据库操作，MySQL类
 * @package Db
 * @author Chengxuan <chengxuan@leju.sina.com.cn>
 * @version #Id:db_mysql.php 2010-5-24 上午10:54:13#
 */

class Cls_Db_Mysql extends Cls_Db_Base {
	/**
	 * 主库连接对象
	 * @var PDO
	 */
	protected $link_master = null;
	
	/**
	 * 从库连接对象
	 * @var PDO
	 */
	protected $link_slave = null;
	
	/**
	 * 连接数据库
	 * @param string $host		主机地址
	 * @param string $username	用户名
	 * @param string $password	密码
	 * @param string $database	数据库名
	 * @param string $port		数据库端口
	 * @return resource
	 */
	public function connect($host, $username, $password, $database, $port=3306)
	{
		$port && $host .= ":{$port}";
		$link = mysql_connect($host, $username, $password, true);
		if(!$link)									$this->error($link);
		if(!mysql_select_db($database, $link))		$this->error($link);
		if(!mysql_set_charset('utf8', $link))		$this->error($link);		
		return $link;
	}
	
	/**
	 * 测试连接是否存在，如果不存在，重新连接
	 * @param string/int $server	根据SQL语句自动选择或强制指定数据库
	 * @return resource
	 */
	public function ping($server)
	{
		!is_int($server) && $server = $this->getServer($server);
		if($server == self::CONN_MASTER)
		{
			$link = & $this->link_master;
		}
		else
		{
			$link = & $this->link_slave;
		}

		
		if($link)
		{
			$status = mysql_ping($link);
		}

		if(!$link || !$status)
		{
			$conf = $this->getConf($server);
			$link = $this->connect($conf['host'], $conf['username'], $conf['password'], $conf['database'], $conf['port']);
		}
		
		return $link;
	}
	
	/**
	 * 查询一句语句，反回查询句柄
	 * @param string $sql			SQL语句
	 * @param array $param			变量参数
	 * @param boolean $is_master		是否强制使用主库
	 * @return PDOStatement
	 */
	public function query($sql, $param=null, $is_master=false)
	{
		$this->sql = $this->makeSQL($sql, $param);
		$server = ($is_master || $this->getServer($this->sql) == self::CONN_MASTER) ? self::CONN_MASTER : self::CONN_SLAVE;

		$link = $this->ping($server);
		if(!is_resource($link)) return false;

		$query = mysql_query($this->sql, $link);
		if($query===false)	$this->error($link);

		return $query;
	}
	
	
	/**
	 * 查询一条语句，返回影响的数据行数
	 * @param string	$sql		SQL语句
	 * @param array		$param		变量参数
	 * @param boolean	$is_master	是否强制使用主库
	 * @return int
	 */
	public function execute($sql, $param=null, $is_master=false)
	{
		$this->sql = $this->makeSQL($sql, $param);
		$server = ($is_master || $this->getServer($this->sql) == self::CONN_MASTER) ? self::CONN_MASTER : self::CONN_SLAVE;
		
		$link = $this->ping($server);
		if(!is_resource($link)) return false;

		$query = mysql_query($this->sql, $link);
		if($query===false)	$this->error($link);

		return mysql_affected_rows($link);
	}
	
	/**
	 * 取得一个数据
	 * @param string		$sql		SQL语句
	 * @param array			$param		变量参数
	 * @param null/string	$column		指定返回字段
	 * @param boolean		$is_master	是否强制使用主库
	 * @return string/boolean
	 */
	public function fetchOne($sql, $param=null, $column=null, $is_master=false)
	{
		if(!$column || is_numeric($column)) {
			$query = $this->query($sql, $param, $is_master);
			return $query ? @mysql_result($query, (int)$column) : $query;
		}
		else
		{
			$result = $this->fetchRow($sql, $param, $is_master, self::FETCH_ASSOC);
			return isset($result[$column]) ? $result[$column] : false;
		}
	}

	/**
	 * 取得一行数据
	 * @param string	$sql			SQL语句
	 * @param array		$param			变量参数
	 * @param boolean	$is_master		是否强制使用主库
	 * @param int		$fetch_type		指定返回数据格式(Cls_Db_Base::FETCH_*)
	 * @return array/boolean
	 */
	public function fetchRow($sql, $param=null, $is_master=false, $fetch_type=self::FETCH_ASSOC)
	{
		$query = $this->query($sql, $param, $is_master);
		return $query ? mysql_fetch_array($query, $this->F($fetch_type)) : $query;
	}	
	
	/**
	 * 取得一列数据
	 * @param string		$sql		SQL语句
	 * @param array			$param		变量参数
	 * @param null/string	$column		指定返回字段
	 * @param boolean		$is_master	是否强制使用主库
	 * @return array
	 */
	public function fetchCol($sql, $param=null, $column=null, $is_master=false)
	{
		$query = mysql_query($sql, $param, $is_master);
		if(!$query) return false;
		$result = array();
		while( $row = mysql_fetch_array($query, MYSQL_BOTH) )
		{
			isset($row[$column]) && $result[] = $row[$column];
		}
		
		return $result;
	}	
	
	/**
	 * 取得所有数据
	 * @param string $sql			SQL语句
	 * @param array $param			变量参数
	 * @param boolean $is_master		是否强制使用主库
	 * @param int $fetch_type		指定返回数据格式(Cls_Db_Base::FETCH_*)
	 * @return array
	 */
	public function fetchAll($sql, $param=null, $is_master=false, $fetch_type=self::FETCH_ASSOC)
	{
		$query = mysql_query($sql, $param, $is_master);
		if(!$query) return false;
		
		$result = array();
		$fetch_type = $this->F($fetch_type);
		while( $row = mysql_fetch_array($query, $fetch_type) )
		{
			$result[] = $row;
		}
		
		return $result;
	}
	
	/**
	 * 取得SELECT查询的数据行数
	 * @param resource $query
	 * @return int/boolean
	 */
	public function numRows($query)
	{
		if(!is_resource($query))
		{
			return false;
		}
		
		return mysql_num_rows($query);
	}
	
	/**
	 * 转换类型，将基类的数据类型转换为PDO的
	 * @param int $self_fetch
	 * @return int
	 */
	protected function F($self_fetch) {
		static $conf = array(
			self::FETCH_ASSOC	=> MYSQL_ASSOC,
			self::FETCH_BOTH	=> MYSQL_BOTH,
			self::FETCH_NUM		=> MYSQL_NUM,
		);
		
		return isset($conf[$self_fetch]) ? $conf[$self_fetch] : PDO::FETCH_ASSOC;
	}
	
	/**
	 * 处理PDO错误
	 * @param resource $link	MySQL连接资源
	 */
	public function error($link)
	{
		$code = mysql_errno($link);
		$info = mysql_error($link);
		$string = "[{$code}] $info [{$this->sql}]";
		
		$this->throwError($string);
	}	
}
